/**
 * 
 */
package com.yullage.ae.core;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Yu-chun Huang
 * 
 */
public class Zone implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * Logger
	 */
	private static final Logger logger = LoggerFactory.getLogger(Zone.class);

	private String zoneName;

	private long totalNeurons = 0;
	private double energy;
	private Set<Path> incomingPaths = new HashSet<Path>();
	private Set<Path> outgoingPaths = new HashSet<Path>();

	private double fullEnergy;

	public Zone(String zoneName) throws IllegalArgumentException {
		if ((zoneName == null) || "".equals(zoneName.trim())) {
			logger.error("Zone name not specified.");
			throw new IllegalArgumentException();
		}

		this.zoneName = zoneName;
	}

	public void init() {
		long numIncomingNeurons = 0;
		long numOutgoingNeurons = 0;

		for (Path path : incomingPaths) {
			numIncomingNeurons += path.getNumNeurons();
		}

		for (Path path : outgoingPaths) {
			numOutgoingNeurons += path.getNumNeurons();
		}

		if (numIncomingNeurons >= numOutgoingNeurons) {
			totalNeurons = numIncomingNeurons;
		} else {
			totalNeurons = numOutgoingNeurons;
		}

		if (totalNeurons < 0) {
			logger.warn("Degenerated zone: {}", zoneName);
		}

		fullEnergy = totalNeurons * Constants.ENERGY_PER_NEURON;
		energy = 0;
	}

	public void trigger(double intensity) {
		addEnergy(intensity * totalNeurons);
	}

	public void fire() {
		for (Path path : outgoingPaths) {
			path.charge(energy * path.getNumNeurons() / (double) totalNeurons);
		}

		energy = 0;
	}

	public String getZoneName() {
		return zoneName;
	}

	public double getTotalNeurons() {
		return totalNeurons;
	}

	public void setTotalNeurons(long totalNeurons) {
		this.totalNeurons = totalNeurons;
	}

	public Set<Path> getIncomingPaths() {
		return incomingPaths;
	}

	public void setIncomingPaths(Set<Path> incomingPaths) {
		this.incomingPaths = incomingPaths;
	}

	public Set<Path> getOutgoingPaths() {
		return outgoingPaths;
	}

	public void setOutgoingPaths(Set<Path> outgoingPaths) {
		this.outgoingPaths = outgoingPaths;
	}

	public double getIntensity() {
		return energy / fullEnergy;
	}

	public double getEnergy() {
		return energy;
	}

	public void setEnergy(double energy) {
		this.energy = energy;
	}

	public void addEnergy(double energy) {
		this.energy += energy;
		if (this.energy > fullEnergy) {
			this.energy = fullEnergy;
		}
	}
}
